package com.trs.publish.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import com.trs.publish.domain.Power;
import com.trs.publish.domain.Role;
import com.trs.publish.repository.PowerRepository;
import com.trs.publish.repository.RoleRepository;


@Component
public class MyInvocationSecurityMetadataSourceService  implements FilterInvocationSecurityMetadataSource {
		
	Logger logger = LoggerFactory.getLogger(getClass());
		
		//权限
		@Autowired
		private PowerRepository powerRepository;
		
		public MyInvocationSecurityMetadataSourceService(PowerRepository powerRepository) {
			this.powerRepository = powerRepository;
			this.loadResourceDefine();
		}
		
		//解析Url的匹配器，框架提供两种选择：一个是AntUrlPathMatcher，另一个是正则匹配
		//private UrlMatcher urlMatcher = new RegexUrlPathMatcher();
		//private UrlMatcher urlMatcher = new AntUrlPathMatcher();spring3.1不支持，用AntPathMatcher替代
		private AntPathMatcher antPathMatcher = new AntPathMatcher();
		
		//该map集合用于存放资源-角色的对应关系
		private  Map<String, Collection<ConfigAttribute>> resourceMap = null;

		/**
		 * 加载相关的资源权限的定义，将定义的权限构建ConfigAttribute对象,存入
		 * Collection<T>泛型集合中，并将该集合作为value。将角色拥有的资源的url作为key,由此
		 * 构建一个map集合将资源对应的角色信息对应存放。
		 */
		private void loadResourceDefine() {
			long startTime = System.currentTimeMillis();
//			roleManager = (RoleManager) this.getBean("roleManager");
			
			resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
			//此集合的作用是存放权限信息的ConfigAttribute实现类对象
			Collection<ConfigAttribute> attributes = null;
			ConfigAttribute cfg;
			List<Power> powersList = powerRepository.getAllPowers();
			if(powersList!=null&&powersList.size()>0) {
				for(Power power:powersList) {
					attributes = new ArrayList<ConfigAttribute>();
					cfg = new SecurityConfig(power.getPowername());
					 //此处只添加了用户的名字，其实还可以添加更多权限的信息，例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
					attributes.add(cfg);
		            //用权限的getPowerurl() 作为resourceMap的key，用ConfigAttribute的集合作为 value，
					resourceMap.put(power.getPowerurl(), attributes);
				}
			}
			
			long endTime = System.currentTimeMillis();
			logger.info("初始化资源URL和角色对应关系耗时"+(endTime-startTime)/1000.0+"秒,包含记录"+resourceMap.size()+"条");
		}

		@Override
		public Collection<ConfigAttribute> getAllConfigAttributes() {
			return null;
		}

		@Override
		public Collection<ConfigAttribute> getAttributes(Object o)
				throws IllegalArgumentException {
			if(resourceMap ==null) {
				loadResourceDefine();
			}
			long startTime = System.currentTimeMillis();
			int count = 0;//url匹配次数
			try {
				// 获取URL的web应用程序的特定片段，不包括任何服务器的名称、上下文路径或servlet的路径
				String url = ((FilterInvocation) o).getRequestUrl();
				Set<String> keySet = resourceMap.keySet();
				for (String key : keySet) {
					count++;
					// pathMatchesUrl方法判断请求的URL是否匹配,如果有匹配的，则返回保存有角色信息的ConfigAttribute对象的集合
					//if (urlMatcher.pathMatchesUrl(key,url)) {
					if(antPathMatcher.match(key, url)){
						Collection<ConfigAttribute> attributes = resourceMap.get(key);
						return attributes;
					}
				}
				//如果没有匹配的返回null，表示此url直接放行
				return null;
			} catch (Exception e) {
				throw new IllegalArgumentException(e);
			}finally{
				long costTime = System.currentTimeMillis()-startTime;
				logger.debug("权限url匹配"+count+"次,耗时:"+costTime/1000.0+"秒");
			}
		}

		@Override
		public boolean supports(Class<?> arg0) {
			return true;
		}
		
	}
